package com.zyl.blog.gateway.exception;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.util.SaResult;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @version v1.0
 * @ProjectName: Personal_Blog
 * @Package: com.zyl.gateway.exception
 * @ClassName: GlobalGatewayExceptionHandler
 * @Description: 全局网关异常处理器
 * @Author FinalFantasy
 * @Date 2025/6/24-15:01
 */
@Slf4j
@Component
@Order(-1)
public class GlobalGatewayExceptionHandler implements ErrorWebExceptionHandler {

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

        // 构建 SaResult 对象
        SaResult saResult = buildSaResult(ex);

        // 设置 HTTP 状态码
        HttpStatus status = resolveHttpStatus(ex);
        response.setStatusCode(status);

        // 记录错误日志
        log.error("[Gateway Error] URI: {} - Method: {} - Error: {}",
                exchange.getRequest().getURI(),
                exchange.getRequest().getMethod(),
                buildLogMessage(ex));

        // 返回统一格式
        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            try {
                return bufferFactory.wrap(JSON.toJSONBytes(saResult));
            } catch (Exception e) {
                log.error("响应数据序列化失败", e);
                return bufferFactory.wrap(JSON.toJSONBytes(SaResult.error("系统错误")));
            }
        }));
    }

    /**
     * 构建 Sa-Token 标准响应格式
     */
    private SaResult buildSaResult(Throwable ex) {
        // 默认错误信息
        String errorMsg = "系统繁忙，请稍后重试";
        int code = 500;

        // 根据异常类型定制响应
        if (ex instanceof NotLoginException) {
            code = 401;
            errorMsg = "登录已失效，请重新登录";
        } else if (ex instanceof NotPermissionException || ex instanceof NotRoleException) {
            code = 403;
            errorMsg = "权限不足，拒绝访问";
        } else if (ex instanceof io.netty.channel.ConnectTimeoutException) {
            code = 504;
            errorMsg = "服务连接超时，请稍后重试";
        } else if (ex.getCause() instanceof java.util.concurrent.TimeoutException) {
            code = 504;
            errorMsg = "服务响应超时，请检查服务状态";
        } else if (ex.getMessage() != null) {
            // 其他已知异常处理
            if (ex.getMessage().contains("Rate limiter")) {
                code = 429;
                errorMsg = "请求过于频繁，请稍后再试";
            } else {
                errorMsg = ex.getMessage();
            }
        }

        return SaResult.get(code, errorMsg, null);
    }

    /**
     * 解析 HTTP 状态码（保持与 SaResult.code 一致）
     */
    private HttpStatus resolveHttpStatus(Throwable ex) {
        if (ex instanceof ResponseStatusException) {
            return (HttpStatus) ((ResponseStatusException) ex).getStatusCode();
        }
        if (ex instanceof NotLoginException) {
            return HttpStatus.UNAUTHORIZED;
        }
        if (ex instanceof NotPermissionException || ex instanceof NotRoleException) {
            return HttpStatus.FORBIDDEN;
        }
        if (ex instanceof io.netty.channel.ConnectTimeoutException ||
                ex.getCause() instanceof java.util.concurrent.TimeoutException) {
            return HttpStatus.GATEWAY_TIMEOUT;
        }
        return HttpStatus.INTERNAL_SERVER_ERROR;
    }

    /**
     * 构建日志详细信息
     */
    private String buildLogMessage(Throwable ex) {
        StringBuilder sb = new StringBuilder();
        sb.append(ex.getClass().getSimpleName())
                .append(": ").append(ex.getMessage());

        // 打印关键堆栈（前3行）
        StackTraceElement[] stack = ex.getStackTrace();
        for (int i = 0; i < Math.min(3, stack.length); i++) {
            sb.append("\n\tat ").append(stack[i]);
        }
        return sb.toString();
    }
}
